package com.ly.desktop;

import javax.swing.*;
import javax.swing.plaf.nimbus.AbstractRegionPainter;
import java.io.*;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLEncoder;
import java.nio.charset.Charset;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.Timer;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Stream;

public class TeachHelper {

    private String resDir;
    private String clsSn;
    private String timeName;
    private File srcDirFile;
    private SimpleDateFormat mmdd = new SimpleDateFormat("MMdd");
    private SimpleDateFormat mmddhhmm = new SimpleDateFormat("MM-dd HH:mm");
    private SimpleDateFormat yyyymmdd = new SimpleDateFormat("yyyyMMdd");
    private Calendar cal = Calendar.getInstance();

    public void init() throws FileNotFoundException, UnsupportedEncodingException {
        while(true){
            refreshClsSn();
            final int i = JOptionPane.showConfirmDialog(null,
                    "请确认当前班级: " + clsSn + " 班 ? " +
                            "\n是:       确认并继续" +
                            "\n否:       修改班级后继续" +
                            "\n取消:  终止操作");
            if(i == JOptionPane.CANCEL_OPTION){
                throw new RuntimeException("用户终止操作");
            } else if (i == JOptionPane.YES_OPTION){
                break;
            }
        }
        String path = TeachHelper.class.getClassLoader().getResource("").getPath();
        File file = new File(path + "/th.cfg");
        Properties props = new Properties();
        String charset = Charset.defaultCharset().name();
        if (file.exists() == false) {
            exec(p -> file.createNewFile());
        }

        try (InputStreamReader fr = new InputStreamReader(new FileInputStream(file), charset)) {
            props.load(fr);
        } catch (IOException e) {
            e.printStackTrace();
        }
        String charset1 = props.getProperty("charset");
        if (charset1 != null && charset.equalsIgnoreCase(charset1) == false) {
            try (InputStreamReader fr = new InputStreamReader(new FileInputStream(file), charset1)) {
                props.load(fr);
                charset = charset1;
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        resDir = props.getProperty("resDir");
        if (resDir == null) {
            resDir = JOptionPane.showInputDialog("请求输入视频保存目录(resDir)");
            if(resDir==null){
                throw new FileNotFoundException("算了吧,不想用了!");
            }
            props.put("resDir", resDir);
            props.put("charset", charset);
            try (OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream(file), charset)) {
                props.store(osw, "save config");
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        srcDirFile = new File(getSrcDir());
        if (srcDirFile.exists() == false) srcDirFile.mkdirs();
        refreshClsSn();
        if (clsSn == null || resDir == null) {
            log("未读取到配置参数!退出系统!");
            System.exit(-1);
        }
        log("charset:\t" + charset);
        log("resDir:\t" + resDir);
    }

    private String getSrcDir(){
        return resDir + "/MP4";
    }

    public void start() throws FileNotFoundException, UnsupportedEncodingException {
        log("视频分发器启动......");
        final String today = yyyymmdd.format(new Date());
        try{
            init();
            new Timer().schedule(new TimerTask() {
                public void run() {
                    action(null);
                }
            }, 0, 600000);
            new Thread(() -> {
                Scanner sc = new Scanner(System.in);
                while (true) {
                    log("手动下载, 请输入下载目录 ( 参考格式[%s_上午], 直接回车将下载当前班级最新文件 ):", today);
                    String s = sc.nextLine();
                    if (s.trim().isEmpty()) {
                        log("下载当前课堂资源!");
                        action(null);
                    } else if (s.matches("\\d{8}_(上午|下午|晚上)") || s.startsWith("getCode")) {
                        action(s);
                    } else {
                        log("目录格式错误[%s]! 参考格式[%s_上午], 现在开始下载当前课堂资源!", s, today);
                    }
                }
            }).start();
        }catch (Exception e){
            log(e.getMessage());
        }
    }

    private void action(String serverPath) {
        refreshClsSn();
        String dirName;
        if (serverPath == null || serverPath.startsWith("getCode")) {
            dirName = buildDirName(new Date());
        } else {
            dirName = buildDirName(serverPath);
        }
        String resDir = TeachHelper.this.resDir + "/" + clsSn + "/" + dirName;
        File resDirFile = new File(resDir);
        if (resDirFile.exists() == false) {
            resDirFile.mkdirs();
        }
        // 直接下载指定目录下的图片
        if (serverPath!=null && serverPath.startsWith("getCode")){
            downToResDir(resDirFile, serverPath);
            return;
        }
        List<String> list = Arrays.asList(resDirFile.list());
        srcDirFile.listFiles(f -> {
            String dn = buildDirName(new Date(f.lastModified()));
            if (dn.equals(dirName)) {
                if (f.getName().matches("\\d+_\\d+\\.\\w+") == false &&
                        list.contains(f.getName()) == false) {
                    copy(f, resDirFile);
                }
            } else {
                if (f.delete()) {
                    log("删除  %s", f);
                }
            }
            return false;
        });
        downToResDir(resDirFile, serverPath);
    }

    private String url(String path) {
        StringBuilder sb = new StringBuilder();
        Pattern p = Pattern.compile("([\u4E00-\u9FA5]+)");
        Matcher m = p.matcher(path);
        int i = 0;
        while (m.find(i)) {
            sb.append(path.substring(i, m.start()));
            exec(pp -> sb.append(URLEncoder.encode(m.group(1), "utf-8")));
            i = m.end();
        }
        if (i > 0) {
            sb.append(path.substring(i, path.length()));
        } else {
            sb.append(path);
        }
        return "http://47.106.66.89:8080/" + sb;
    }

    private void downToResDir(File resDirFile, String serverPath) {
        String url;
        if(serverPath!=null && serverPath.startsWith("getCode")){
            url = serverPath;
        } else {
            url = "getCode?clsSn=" + clsSn + "&dir=";
            // serverPath 为空则下载当前最新目录的文件
            if (serverPath != null) {
                url += serverPath;
            }
        }
        url = url(url);
        String res = get(url);
        if (!res.contains("[")) {
            log(url+" 没有资源!");
            return;
        }
        final String[] strings = parseFiles(res);
        if (strings.length==0) {
            log(url+" 没有资源!");
            return;
        }
        Stream<String> downls = Arrays.stream(strings);
        downls.forEach(f -> {
            if (!f.matches(".+\\.mp4")) {
                if (f.matches("(.+)_m(\\.\\w+)")) {
                    f = f.replaceAll("(.+)_m(\\.\\w+)", "$1$2");
                }
                String _f = f.replaceAll(".+/(.+\\.\\w+)", "$1");
                ;
                Stream<String> locals = Arrays.stream(resDirFile.list());
                if (locals.noneMatch(lf -> lf.equals(_f))) {
                    String filepath = resDirFile.getAbsolutePath() + "/" +
                            f.replaceAll(".+/(.+)", "$1");
                    try {
                        String downpath = url(f);
                        log("下载 %s 到 %s", f, filepath);
                        get(downpath, filepath);
                    } catch (Exception e) {
                        e.printStackTrace();
                        log(" 失败! 原因: " + e.getCause().getMessage());
                    }
                }
            }
        });
    }

    private String[] parseFiles(String s) {
        Pattern p = Pattern.compile("([^\"]+\\.\\w+)");
        Matcher m = p.matcher(s);
        int index = 0;
        List<String> list = new ArrayList<>();
        while (m.find(index)) {
            if (m.group(1).contains("/")) {
                list.add(m.group(1));
            }
            index = m.end();
        }
        return list.toArray(new String[list.size()]);
    }

    private void copy(File f, File resDirFile) {
        File rf = new File(resDirFile, f.getName());
        try (FileInputStream fis = new FileInputStream(f);
             FileOutputStream fos = new FileOutputStream(rf)) {
            byte[] bytes = new byte[1024];
            int count;
            while ((count = fis.read(bytes)) > 0) {
                fos.write(bytes, 0, count);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        log("拷贝 %s 到 %s", f, rf);
    }

    public String buildDirName(String serverDir) {
        return serverDir.replaceAll("\\d{4}(\\d{4})_(上午|下午|晚上)","$1$2");
    }
    public String buildDirName(Date date) {
        cal.setTime(date);
        int h = cal.get(Calendar.HOUR_OF_DAY);
        String timeName;
        if (h >= 18) {
            timeName = "晚上";
        } else if (h >= 13) {
            timeName = "下午";
        } else {
            timeName = "上午";
        }
        timeName = mmdd.format(date) + timeName;
        if (!timeName.equals(this.timeName)) {
            this.timeName = timeName;
            refreshClsSn();
        }
        return this.timeName;
    }

    public void refreshClsSn() {
        String ret = get(url("user/get?id=1&fields=clsSn"));
        String clsSn = ret.replaceAll("\\D", "");
        if (clsSn.matches("\\d+") && clsSn.equals(this.clsSn) == false) {
            if (this.clsSn != null) {
                log("切换班级:\t\t%s => %s", this.clsSn, clsSn);
            } else {
                log("设置班级:\t\t%s", clsSn);
            }
            this.clsSn = clsSn;
        }
    }

    public static void main(String[] args) throws FileNotFoundException, UnsupportedEncodingException {
        new TeachHelper().start();
    }

    public static <T> T get(String path, String... exp) {
        return (T) call(p -> {
            URL url = new URL(path);
            URLConnection conn = url.openConnection();
            String contentType = conn.getContentType();
            InputStream in = conn.getInputStream();
            if ("application/json".equals(contentType)) {
                InputStreamReader isr = new InputStreamReader(in,"utf-8");
                return call(pp -> {
                    StringBuilder sb = new StringBuilder();
                    char[] chars = new char[1024];
                    int c;
                    while ((c = isr.read(chars)) > 0) {
                        sb.append(new String(chars, 0, c));
                    }
                    return sb.toString();
                }, isr);
            } else {
                String filepath = exp[0];
                File file = new File(filepath);
                FileOutputStream out = new FileOutputStream(file);
                exec(pp -> {
                    byte[] bytes = new byte[1024];
                    int c;
                    while ((c = in.read(bytes)) > 0) {
                        out.write(bytes, 0, c);
                    }
                }, in, out);
                return file;
            }
        });
    }


    public static interface Caller<T> {
        T handle(Object... params) throws Exception;
    }

    public static <T> T call(Caller<T> caller, Object... params) {
        try {
            return caller.handle(params);
        } catch (RuntimeException e) {
            throw e;
        } catch (Exception e) {
            throw new RuntimeException("call error", e);
        } finally {
            for (Object p : params) {
                if (p instanceof AutoCloseable) {
                    try {
                        ((AutoCloseable) p).close();
                    } catch (Exception e) {
                        throw new RuntimeException("close error", e);
                    }
                }
            }
        }
    }

    public static interface Execer {
        void handle(Object... params) throws Exception;
    }

    public static void exec(Execer execer, Object... params) {
        call(p -> {
            execer.handle(p);
            return null;
        }, params);
    }

    private void log(String msg, Object... params) {
        String d = mmddhhmm.format(new Date()) + " : ";
        if(params.length>0){
            System.out.printf(d + msg + "\n", params);
        } else {
            System.out.println(d + msg);
        }
    }
}
